
#include "dict.h"
#include "server.h"
#include "session.h"
#include "http_context.h"
#include "http_request.h"
#include "http_response.h"
#include "http_server.h"
#include "zmalloc.h"
#include "mylog.h"

#include <string.h>
#include <stdio.h>

#define NOT_FINDCRL	-1
static int findCRL(const char* buf, int bufLen){

	if (!buf || bufLen <= 0){
		return NOT_FINDCRL;
	}

	int i;
	for (i = 0; i < bufLen - 1; i++) {
		if (buf[i] == '\r') {
			if (buf[ i + 1] == '\n') {
				return i + 1;
			}
		}
	}

	return NOT_FINDCRL;
}

static int findSpace(const char* buf, int bufLen) {
	if (!buf || bufLen <= 0){
		return NOT_FINDCRL;
	}

	int i;
	for (i = 0; i < bufLen; i++){
		if (buf[i] == ' '){
			return i;
		}
	}
	return NOT_FINDCRL;
}

static int equal(const char* buf, int bufLen, char* cmpBuf) {
	if (!buf || !cmpBuf) {
		return -1;
	}

	int cmpBufLen = strlen(cmpBuf);
	int i;
	for (i = 0; i < bufLen || i < cmpBufLen; i++) {
		if (buf[i] != cmpBuf[i]) {
			return -1;
		}
	}

	return 0;
}

static int addHttpContext(struct httpServer* hServer, struct session* session) {
	if (!hServer || !session) {
		return -1;
	}

	struct HttpContext* httpContext = (struct HttpContext*)initHttpContext();
	if (!httpContext) {
		return -2;
	}

	pthread_mutex_lock(&hServer->sessionDictLock);
	dictAdd(hServer->sessionDict, (void*)session, (void*)httpContext);
	pthread_mutex_unlock(&hServer->sessionDictLock);
}

static struct HttpContext* getHttpContext(struct httpServer* hServer, struct session* session) {
	if (!hServer || !session) {
		return NULL;
	}

	struct HttpContext* httpContext = NULL;
	pthread_mutex_lock(&hServer->sessionDictLock);
	httpContext = (struct HttpContext* )dictFetchValue(hServer->sessionDict,  (void*)session);
	pthread_mutex_unlock(&hServer->sessionDictLock);

	return httpContext;
}

static int removeHttpContext(struct httpServer* hServer, struct session* session) {
	mylog(LOG_INFO, "remove httpContext!!!");
	if (!hServer || !session) {
		return -1;
	}

	struct HttpContext* httpContext = NULL;

	pthread_mutex_lock(&hServer->sessionDictLock);
	httpContext = (struct HttpContext* )dictFetchValue(hServer->sessionDict,  (void*)session);
	dictDeleteNoFree(hServer->sessionDict, (void*)session);
	pthread_mutex_unlock(&hServer->sessionDictLock);

	destroyHttpContext(httpContext);
	return 0;
}

static int processRequestLine(const char* begin, int beginLen, struct HttpContext* context){
	mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
	int succeed = -1;
	const char* start = begin;
	int spacePos = findSpace(start, beginLen);
	if (spacePos == NOT_FINDCRL) {
		return succeed;
	}
	mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
	int ret = setRequestMethod(context->httpRequest, start, spacePos);
	if (ret == 0) {
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		char* buf = ((char*)begin + spacePos);
		int bufLen = beginLen - spacePos;
		spacePos = findSpace(buf, beginLen - spacePos);
		if (spacePos == NOT_FINDCRL) {
			return succeed;
		}
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		ret = setRequestPath(context->httpRequest, buf, bufLen);
		if (ret == NOT_FINDCRL) {
			return succeed;
		}
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		buf += spacePos;
		bufLen -= spacePos;
		if (bufLen != 8) {
			return succeed;
		}
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		return equal(buf, bufLen, "HTTP/1.1");
	}

	return succeed;
}

static int parseRequest(struct httpServer* hServer, struct session* session) {
	mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
	if (!session) {
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		return -1;
	}

	struct HttpContext* httpContext = getHttpContext(hServer,  session);
	if (!httpContext) {
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		return -2;
	}

	int hasMore = 1;
	while(hasMore) {
		int byteCount = getReadableBytesBuffer(session->readBuffer);
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		if (httpContext->state == ExpectRequestLine) {
			mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
			int pos = findCRL((char* )getReadIndexBuffer(session->readBuffer), byteCount);
			if (pos == NOT_FINDCRL) {
				hasMore = 0;
			} else {
				mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
				int ret = processRequestLine((char* )getReadIndexBuffer(session->readBuffer), pos, httpContext);
				if (ret == 0) {
					mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
					moveReadIndexBuffer(session->readBuffer, pos);
					httpContext->state == ExpectHeaders;
				} else {
					mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
					hasMore = 0;
				}
			}
		} else if (httpContext->state == ExpectHeaders) {
			mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
			hasMore = 0;
		}
	}

	return 0;
}

static void onHttpConnection(struct session* session) {
	mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
	if (session->privateData) {
		struct httpServer* hServer = (struct httpServer*)session->privateData;
		if (!hServer) {
			mylog(LOG_WARNING, "onHttpConnection httpServer is null!!!");
			return;
		}

		addHttpContext(hServer, session);
	} else {
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
	}
}

static void onRequest(struct httpServer* hServer, struct session* session, struct HttpRequest* request) {
	mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
	if (!hServer || !session || !request) {
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		return;
	}

	struct HttpResponse* httpResponse = initHttpResponse();
	if (!httpResponse) {
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		return;
	}

	hServer->onHttpCallBack(request, httpResponse);
	char* sendBuf = NULL;
	int sendBufLen = 0;
	sendBuf = appendToBuffer(httpResponse, &sendBufLen);
	writeSession(session, sendBuf, sendBufLen);
	mylog(LOG_INFO, "%s [%d]", sendBuf, sendBufLen);
	if (sendBuf) {
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		zfree(sendBuf);
	}

	destroyHttpResponse(httpResponse);
}

static RetHandle onHttpData(struct session* session) {
	mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
	if (session->privateData) {
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		struct httpServer* hServer = (struct httpServer*)session->privateData;
		int ret = parseRequest(hServer, session);
		if (ret == 0) {
			if (hServer->onHttpCallBack) {
				struct HttpContext* httpContext = getHttpContext(hServer, session);
				if (!httpContext) {
					mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
					return CLOSE_HANDLE;
				}
				onRequest(hServer, session, httpContext->httpRequest);

				removeHttpContext(hServer, session);
			}
		}
	} else {
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
	}
	return CLOSE_HANDLE;
}

static void onHttpDisConnection(struct session* session) {
	mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
	if (session->privateData) {
		mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
		struct httpServer* hServer = (struct httpServer*)session->privateData;
		removeHttpContext(hServer, session);
	}
}

static void onHttpCompConnetcionCallBack(struct session* session) {
	mylog(LOG_INFO, "%s, %d", __FUNCTION__, __LINE__);
	if (session->privateData) {
		mylog(LOG_INFO, "%s, %d session[id], comp callback!!!", __FUNCTION__, __LINE__, session->id);
		//shutdownSession(session);
	}
}

int dictEncObjKeyCompare(void *privdata, const void *key1, const void *key2)
{
	struct session* session1 = (struct session* )key1;
	struct session* session2 = (struct session* )key2;

    return session1->id == session1->id;
}

unsigned int dictEncObjHash(const void *key) {
	struct session* session = (struct session*)key;
	return session->id;
}

/* sessionDict type */
dictType sessionDictType = {
    dictEncObjHash,            /* hash function */
    NULL,                      /* key dup */
    NULL,                      /* val dup */
    dictEncObjKeyCompare,      /* key compare */
    NULL, /* key destructor */
    NULL                       /* val destructor */
};


struct httpServer* initHttpServer(struct core* c, char *err, int port, char *bindaddr) {
	struct httpServer* hServer = (struct httpServer*) zcalloc(sizeof(struct httpServer));
	if (!hServer) {
		return NULL;
	}

	hServer->pServer = initServer(c, err, port, bindaddr);
	if (!hServer->pServer) {
		zfree(hServer);
		return NULL;
	}

	pthread_mutex_init(&hServer->sessionDictLock, NULL);

	hServer->sessionDict = dictCreate(&sessionDictType,NULL);
	if (!hServer->sessionDict) {
		destroyServer(hServer->pServer);
		zfree(hServer);
		return NULL;
	}

	hServer->pServer->serverCallBack->onConnectionCallBack = onHttpConnection;
	hServer->pServer->serverCallBack->onDataCallBack = onHttpData;
	hServer->pServer->serverCallBack->onDisConnectionCallBack = onHttpDisConnection;
	hServer->pServer->serverCallBack->onWriteCompleteConnectionCallBack = onHttpCompConnetcionCallBack;
	hServer->pServer->privateData = hServer;

	addServerCore(c, hServer->pServer);
	return hServer;
}

void destroyHttpServer(struct httpServer* hServer) {
	if (!hServer) {
		return;
	}

	if (hServer->pServer) {
		destroyServer(hServer->pServer);
	}

	pthread_mutex_lock(&hServer->sessionDictLock);
	if (hServer->sessionDict) {
		mylog(LOG_INFO, "DESTRORY===========");
        dictIterator *di;
        dictEntry *setele;
	    di = dictGetIterator(hServer->sessionDict);
	    while((setele = dictNext(di)) != NULL) {
	    	struct HttpContext* httpContext = NULL;
	    	httpContext = dictGetEntryVal(setele);
	    	destroyHttpContext(httpContext);
	    }
	    dictReleaseIterator(di);
		mylog(LOG_INFO, "DESTRORY===========");
		dictRelease(hServer->sessionDict);
	}
	pthread_mutex_unlock(&hServer->sessionDictLock);

	pthread_mutex_destroy(&hServer->sessionDictLock);

	zfree(hServer);
	return;
}
